/*!
 * \file IniHelper.hpp
 * \project	WonderTrader
 *
 * \author Wesley
 * \date 2020/03/30
 * 
 * \brief INI文件操作辅助类，基于boost的property_tree实现，支持跨平台使用
 * 
 * \details 提供完整的INI文件读写功能，包括：
 *          - INI文件的加载和保存
 *          - 节（Section）和键值对的操作
 *          - 多种数据类型的读写支持
 *          - 异常安全的操作接口
 *          - 批量数据读取功能
 *          
 *          基于boost::property_tree实现，
 *          提供了简洁易用的INI配置文件操作接口，
 *          广泛用于WonderTrader框架的配置管理。
 */
#pragma once

#include <string>
#include <vector>
#include <map>

#include <boost/property_tree/ptree.hpp>  
#include <boost/property_tree/ini_parser.hpp>

/// \brief 字段数组类型定义，用于存储字符串列表
typedef std::vector<std::string>			FieldArray;

/// \brief 字段映射类型定义，用于存储键值对映射
typedef std::map<std::string, std::string>	FieldMap;

/*!
 * \brief INI文件操作辅助类
 * 
 * \details 封装了INI文件的常用操作，提供了：
 *          - 文件加载和保存功能
 *          - 节和键值的增删改查
 *          - 多种基础数据类型的读写
 *          - 批量数据读取接口
 *          - 异常安全的操作机制
 *          
 *          使用boost::property_tree作为底层实现，
 *          确保了跨平台兼容性和稳定性。
 */
class IniHelper
{
private:
	boost::property_tree::ptree	_root;      ///< 属性树根节点，存储整个INI文件结构
	std::string					_fname;     ///< 当前加载的文件名
	bool						_loaded;    ///< 文件是否已成功加载的标志

	/// \brief 键路径的最大长度限制
	static const uint32_t MAX_KEY_LENGTH = 256;

public:
	/*!
	 * \brief 默认构造函数
	 * 
	 * \details 初始化IniHelper对象，设置加载状态为false
	 */
	IniHelper(): _loaded(false){}

	/*!
	 * \brief 加载INI文件
	 * 
	 * \details 从指定路径加载INI文件到内存中，
	 *          使用boost::property_tree::ini_parser进行解析，
	 *          异常安全，解析失败不会抛出异常。
	 * 
	 * \param szFile INI文件的完整路径
	 * 
	 * \note 即使文件不存在或解析失败，_loaded也会被设置为true
	 */
	void	load(const char* szFile)
	{
		_fname = szFile;
		try
		{
			boost::property_tree::ini_parser::read_ini(szFile, _root);
		}
		catch(...)
		{
			// 忽略加载错误，保持程序稳定性
		}
		
		_loaded = true;
	}

	/*!
	 * \brief 保存INI文件
	 * 
	 * \details 将内存中的配置数据保存到文件，
	 *          可以保存到原文件或指定的新文件。
	 * 
	 * \param filename 目标文件名，为空则保存到原文件
	 * 
	 * \note 使用boost::property_tree::ini_parser进行序列化
	 */
	void	save(const char* filename = "")
	{
		if (strlen(filename) > 0)
			boost::property_tree::ini_parser::write_ini(filename, _root);
		else
			boost::property_tree::ini_parser::write_ini(_fname.c_str(), _root);
	}

	/*!
	 * \brief 检查文件是否已加载
	 * 
	 * \return 返回true表示已调用load方法
	 * 
	 * \note 即使加载失败，此方法也会返回true
	 */
	inline bool isLoaded() const{ return _loaded; }

public:
	/*!
	 * \brief 删除指定的键值对
	 * 
	 * \details 从指定节中删除指定的键值对，
	 *          操作异常安全，键不存在时不会抛出异常。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 */
	void	removeValue(const char* szSec, const char* szKey)
	{
		try
		{
			boost::property_tree::ptree& sec = _root.get_child(szSec);
			sec.erase(szKey);
		}
		catch (...)
		{
			// 忽略删除错误（如节或键不存在）
		}
	}

	/*!
	 * \brief 删除整个节
	 * 
	 * \details 删除指定的节及其所有子键值对，
	 *          操作异常安全，节不存在时不会抛出异常。
	 * 
	 * \param szSec 要删除的节名称
	 */
	void	removeSection(const char* szSec)
	{
		try
		{
			_root.erase(szSec);
		}
		catch (...)
		{
			// 忽略删除错误（如节不存在）
		}
	}

	/*!
	 * \brief 通用值读取模板方法
	 * 
	 * \details 根据指定路径读取值，支持任意类型T，
	 *          路径格式为"section.key"，
	 *          异常安全，读取失败时返回默认值。
	 * 
	 * \tparam T 要读取的数据类型
	 * \param szPath 完整的键路径（格式：section.key）
	 * \param defVal 默认值，读取失败时返回
	 * \return 读取到的值或默认值
	 */
	template<class T>
	T	readValue(const char* szPath, T defVal)
	{
		try
		{
			return _root.get<T>(szPath, defVal);
		}
		catch (...)
		{
			return defVal;
		}
	}

	/*!
	 * \brief 读取字符串值
	 * 
	 * \details 从指定节和键读取字符串值，
	 *          内部构造完整路径后调用readValue模板方法。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param defVal 默认值，读取失败时返回
	 * \return 读取到的字符串值或默认值
	 */
	std::string	readString(const char* szSec, const char* szKey, const char* defVal = "")
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		return readValue<std::string>(path, defVal);
	}

	/*!
	 * \brief 读取整数值
	 * 
	 * \details 从指定节和键读取整数值，
	 *          支持自动类型转换。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param defVal 默认值，读取失败时返回
	 * \return 读取到的整数值或默认值
	 */
	int			readInt(const char* szSec, const char* szKey, int defVal = 0)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		return readValue<int>(path, defVal);
	}

	/*!
	 * \brief 读取无符号整数值
	 * 
	 * \details 从指定节和键读取无符号整数值，
	 *          支持自动类型转换。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param defVal 默认值，读取失败时返回
	 * \return 读取到的无符号整数值或默认值
	 */
	uint32_t	readUInt(const char* szSec, const char* szKey, uint32_t defVal = 0)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		return readValue<uint32_t>(path, defVal);
	}

	/*!
	 * \brief 读取布尔值
	 * 
	 * \details 从指定节和键读取布尔值，
	 *          支持多种布尔表示格式的解析。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param defVal 默认值，读取失败时返回
	 * \return 读取到的布尔值或默认值
	 */
	bool		readBool(const char* szSec, const char* szKey, bool defVal = false)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		return readValue<bool>(path, defVal);
	}

	/*!
	 * \brief 读取双精度浮点数值
	 * 
	 * \details 从指定节和键读取双精度浮点数，
	 *          支持科学计数法等多种数值格式。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param defVal 默认值，读取失败时返回
	 * \return 读取到的双精度浮点数或默认值
	 */
	double		readDouble(const char* szSec, const char* szKey, double defVal = 0.0)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		return readValue<double>(path, defVal);
	}

	/*!
	 * \brief 读取所有节名称
	 * 
	 * \details 遍历INI文件获取所有顶级节的名称，
	 *          结果存储在传入的字符串数组中。
	 * 
	 * \param aySection 用于存储节名称的字符串数组（输出参数）
	 * \return 返回节的总数量
	 */
	int			readSections(FieldArray &aySection)
	{
		for (auto it = _root.begin(); it != _root.end(); it++)
		{
			aySection.emplace_back(it->first.data());
		}

		return _root.size();
	}

	/*!
	 * \brief 读取指定节的所有键名
	 * 
	 * \details 获取指定节下所有键的名称列表，
	 *          用于遍历节的内容。
	 * 
	 * \param szSec 节名称
	 * \param ayKey 用于存储键名的字符串数组（输出参数）
	 * \return 返回键的数量，节不存在时返回0
	 */
	int			readSecKeyArray(const char* szSec, FieldArray &ayKey)
	{
		try
		{
			const boost::property_tree::ptree& _sec = _root.get_child(szSec);
			for (auto it = _sec.begin(); it != _sec.end(); it++)
			{
				ayKey.emplace_back(it->first.data());
			}

			return _sec.size();
		}
		catch (...)
		{
			return 0;
		}
		
	}

	/*!
	 * \brief 读取指定节的所有键值对
	 * 
	 * \details 获取指定节下所有键名和对应值，
	 *          键名和值分别存储在两个对应的数组中。
	 * 
	 * \param szSec 节名称
	 * \param ayKey 用于存储键名的字符串数组（输出参数）
	 * \param ayVal 用于存储键值的字符串数组（输出参数）
	 * \return 返回键值对的数量，节不存在时返回0
	 * 
	 * \note ayKey和ayVal数组的索引是对应的
	 */
	int			readSecKeyValArray(const char* szSec, FieldArray &ayKey, FieldArray &ayVal)
	{
		try
		{
			const boost::property_tree::ptree& _sec = _root.get_child(szSec);
			for (auto it = _sec.begin(); it != _sec.end(); it++)
			{
				ayKey.emplace_back(it->first.data());
				ayVal.emplace_back(it->second.data());
			}

			return _sec.size();
		}
		catch (...)
		{
			return 0;
		}
	}

	/*!
	 * \brief 通用值写入模板方法
	 * 
	 * \details 向指定路径写入值，支持任意类型T，
	 *          路径格式为"section.key"，
	 *          如果节不存在会自动创建。
	 * 
	 * \tparam T 要写入的数据类型
	 * \param szPath 完整的键路径（格式：section.key）
	 * \param val 要写入的值
	 */
	template<class T>
	void		writeValue(const char* szPath, T val)
	{
		_root.put<T>(szPath, val);
	}

	/*!
	 * \brief 写入字符串值
	 * 
	 * \details 向指定节和键写入字符串值，
	 *          内部构造完整路径后调用writeValue模板方法。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param val 要写入的字符串值
	 */
	void		writeString(const char* szSec, const char* szKey, const char* val)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		writeValue<std::string>(path, val);
	}

	/*!
	 * \brief 写入整数值
	 * 
	 * \details 向指定节和键写入整数值，
	 *          自动进行类型转换和格式化。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param val 要写入的整数值
	 */
	void		writeInt(const char* szSec, const char* szKey, int val)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		writeValue<int>(path, val);
	}

	/*!
	 * \brief 写入无符号整数值
	 * 
	 * \details 向指定节和键写入无符号整数值，
	 *          自动进行类型转换和格式化。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param val 要写入的无符号整数值
	 */
	void		writeUInt(const char* szSec, const char* szKey, uint32_t val)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		writeValue<uint32_t>(path, val);
	}

	/*!
	 * \brief 写入布尔值
	 * 
	 * \details 向指定节和键写入布尔值，
	 *          自动转换为字符串表示。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param val 要写入的布尔值
	 */
	void		writeBool(const char* szSec, const char* szKey, bool val)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		writeValue<bool>(path, val);
	}

	/*!
	 * \brief 写入双精度浮点数值
	 * 
	 * \details 向指定节和键写入双精度浮点数，
	 *          保持数值精度和格式。
	 * 
	 * \param szSec 节名称
	 * \param szKey 键名称
	 * \param val 要写入的双精度浮点数值
	 */
	void		writeDouble(const char* szSec, const char* szKey, double val)
	{
		static char path[MAX_KEY_LENGTH] = { 0 };
		snprintf(path, MAX_KEY_LENGTH, "%s.%s", szSec, szKey);
		writeValue<double>(path, val);
	}
};